package com.ews.onlineexchange.service;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Random;

import javax.annotation.Resource;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;
import javax.websocket.DeploymentException;

import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.hibernate.CacheMode;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import com.ews.onlineexchange.app.entity.BulletinStat;
import com.ews.onlineexchange.controller.SuccessBulletinList;
import com.ews.onlineexchange.model.Bulletin;
import com.ews.onlineexchange.model.BulletinDiscardRecord;
import com.ews.onlineexchange.model.ChooseType;
import com.ews.onlineexchange.model.DataEntity;
import com.ews.onlineexchange.model.QualificationGroup;
import com.ews.onlineexchange.model.ServiceSupplier;
import com.ews.onlineexchange.model.Trade;
import com.ews.onlineexchange.model.TradeStatus;
import com.ews.onlineexchange.model.User;
import com.ews.onlineexchange.service.comparator.AverageComparator;
import com.ews.onlineexchange.service.comparator.BestChooseScoreComparator;
import com.ews.onlineexchange.service.comparator.PriceComparator;
import com.ews.onlineexchange.service.comparator.ScoreComparator;
import com.ews.onlineexchange.service.qualifier.BestBulletinAgreeEvent;
import com.ews.onlineexchange.service.qualifier.BestBulletinProcessEvent;
import com.ews.onlineexchange.service.qualifier.BulletinAgreeEvent;
import com.ews.onlineexchange.service.qualifier.BulletinAuditEvent;
import com.ews.onlineexchange.service.qualifier.BulletinPassEvent;
import com.ews.onlineexchange.service.qualifier.EndChooseWsEvent;
import com.ews.onlineexchange.service.qualifier.MessagePushWsEvent;
import com.ews.onlineexchange.service.qualifier.SendWsEvent;
import com.ews.onlineexchange.service.schedule.BulletinAutoPassTimer;
import com.ews.onlineexchange.service.schedule.BulletinOverTimer;
import com.ews.onlineexchange.service.test.BulletinEndClientEndPointService;
import com.ews.onlineexchange.util.DateUtil;
import com.ews.onlineexchange.vo.BulletinSelectItem;

@Stateless
@Named
public class BulletinService {

//	private static final int SEED_COUNT = 3;
	
	@Inject
	EntityManager entityManager;
	
	@Inject
	@BulletinAuditEvent
	Event<Bulletin> bulletinAuditEvent;
	
	@Inject
	@BulletinAgreeEvent
	Event<Bulletin> bulletinAgreeEvent;
	
	@Inject
	@BestBulletinAgreeEvent
	Event<Bulletin> bestBulletinAgreeEvent;
	
	@Inject
	@BestBulletinProcessEvent
	Event<Bulletin> bestBulletinProcessEvent;
	
	
	@Inject
	@BulletinPassEvent
	Event<Bulletin> bulletinPassEvent;
	
	@Inject
	@SendWsEvent
	Event<Bulletin> sendWsEvent;
	
	@Inject
	@EndChooseWsEvent
	Event<Bulletin> endChooseWsEvent;
	
	@Inject
	SupplierSelector supplierSelector;
	
	@Inject
	BulletinEndClientEndPointService bulletinEndClientEndPointService;
	
	@Inject
	BlackListService blackListService;
	
	@Inject
	SuccessBulletinList successBulletinList;
	
	@Inject
	BulletinOverTimer bulletinOverTimer;
	
	@Inject
	BulletinAutoPassTimer bulletinAutoPassTimer;	
	
	@Resource SessionContext ctx;
	
	public String audit(Bulletin bulletin) {
		bulletin.setAuditTime(new Date());
		bulletin.setAuditResult(true);
		bulletin.setAuditor(ctx.getCallerPrincipal().getName());		
		/*
		 * 如果审核时已经超过截至报名时间，直接设置为流拍
		 */
		if (isLate(bulletin)) {
			setOverFields(bulletin);
			bulletin.setTradeStatus(TradeStatus.Passed);
			smsService.addLatePassMessageToBuyer(bulletin);
		}
		
		entityManager.merge(bulletin);		
		bulletinAuditEvent.fire(bulletin);
		
		//中介邀请消息推送
		String message = messgaePushService.addInviteMessageToSupplier(bulletin);
		messagePushWsEvent.fire(message);
		
		return "auditted";
	}
	
	public String unaudit(Bulletin bulletin) {
		bulletin.setAuditResult( null );
		entityManager.merge( bulletin );
		bulletinAutoPassTimer.cancelBulletinTimer( bulletin );
		return "unauditted";
	}
	
	
	private boolean isLate(Bulletin bulletin) {
		return bulletin.getChooseTime().before(new Date());		
	}
	
	public void endBulletinManual(Bulletin bulletin) {
//		Trade successTrade =  bulletin.getSuccessTrade();
		//ServiceSupplier supplier = successTrade.getSupplier();
		//直接选取成交的时候，若中介为黑名单，则将公告设为流拍。
//		if(blackListService.validateUserInBlackList(supplier.getCnname(), supplier.getCreditCode())) {
//			bulletin.setTradeStatus(TradeStatus.Passed);
//			bulletin.setSuccessTrade(null);
//			setOverFields(bulletin);
//			entityManager.merge(bulletin);
//			entityManager.flush();
//		}else {
			bulletin.setTradeStatus(TradeStatus.Agreed);
//			setWinnerId(bulletin);
			setOverFields(bulletin);
			entityManager.merge(bulletin);
			entityManager.flush();
			bulletinAgreeEvent.fire(bulletin);
//			sendWsEvent.fire(bulletin);
//		}
		
	}
	
	public void setWinnerId(Bulletin bulletin) {
		Trade successTrade = bulletin.getSuccessTrade();
		if(successTrade != null) bulletin.setWinnerId(successTrade.getSupplier().getId()); 
		
	}
	@Inject
	@MessagePushWsEvent
	Event<String> messagePushWsEvent;
	
	@Inject
	MessgaePushService messgaePushService;
	@Inject
	RandomChooseService  randomChooseService;
	
	
	@Inject
	SmsService smsService;
	public void endBulletin(Bulletin bulletin) {
		bulletin = entityManager.find(Bulletin.class, bulletin.getId());
		if(bulletin.getAuditResult() == null || !bulletin.getAuditResult()) return ;
		List<Trade> verifiedTrades = bulletin.getVerifiedTrades();
		if (verifiedTrades.isEmpty()) {
			bulletinPassEvent.fire(bulletin);
			setOverFields(bulletin);
			bulletin.setTradeStatus(TradeStatus.Passed);
			entityManager.merge(bulletin);
			return;
		}
		List<Trade> pricedTrades = bulletin.getPricedTrades();
		ChooseType chooseType = bulletin.getWayOfChoose();
		if (chooseType.equals(ChooseType.Random)) {
			int count = verifiedTrades.size();	
			if (count > 0) {
//				int random = BulletinService.getRandom(count);
//				Collections.sort(verifiedTrades, new RandomComparator());
//				bulletin.setSuccessTrade(verifiedTrades.get(0));
				//根据比重选取
				bulletin.setSuccessTrade(randomChooseService.getRandomTrade( verifiedTrades));
				bulletin.setTradeStatus(TradeStatus.Agreed);
				setOverFields(bulletin);
				entityManager.merge(bulletin);
				bulletinAgreeEvent.fire(bulletin);
			} else {
				bulletin.setTradeStatus(TradeStatus.Passed);
				bulletin.setReChooseReason("因无中介报名，此公告已自动流拍！");
				setOverFields(bulletin);
				entityManager.merge(bulletin);
				bulletinPassEvent.fire(bulletin);
			}
			
		} else if (chooseType.equals(ChooseType.Best)) {
			short candidateTradeCount  = bulletin.getCandidateTradeCount();
			int count = verifiedTrades.size();	
			if (count > 0) {
				Collections.sort(verifiedTrades, new ScoreComparator());
				if (verifiedTrades.size() >= candidateTradeCount) {
					bulletin.setTradeStatus(TradeStatus.Waiting);
					bulletin.setBestTrades(verifiedTrades.subList(0, candidateTradeCount));
				} else {//当不满足指定报名家数时，直接流拍
					if ( bulletin.isAutoPass() ) {
						bulletin.setBestTrades(verifiedTrades);
						bulletin.setTradeStatus(TradeStatus.Passed);
						bulletin.setReChooseReason("因中介报名数量，不满足采购方预期中介报名数量，此公告已自动流拍！");
					} else {
						bulletin.setTradeStatus(TradeStatus.Waiting);
						bulletin.setBestTrades(verifiedTrades);
					}
				}
				StringBuilder bestTradesSupplierNames = new StringBuilder();
				for ( Trade trade : bulletin.getBestTrades() ) {
					bestTradesSupplierNames.append( trade.getSupplier().getCnname() ).append( "," );
				}
				bulletin.setBestTradeSupplierNames( bestTradesSupplierNames.substring( 0, bestTradesSupplierNames.length() - 1 ).toString() );
				setOverFields(bulletin);
				entityManager.merge(bulletin);
				// 发起审核工作流
				bestBulletinAgreeEvent.fire(bulletin);
//				bestBulletinProcessEvent.fire(bulletin);//此方法已在bestBulletinAgreeObserver中执行
			} else {
				bulletin.setTradeStatus(TradeStatus.Passed);
				bulletin.setReChooseReason("因无中介报名，此公告已自动流拍！");
				setOverFields(bulletin);
				entityManager.merge(bulletin);
				bulletinPassEvent.fire(bulletin);
			}
		} else {
			if (chooseType.equals(ChooseType.Price)) {
				int count = pricedTrades.size();	
				if (count > 0) {
					Collections.sort(pricedTrades, new PriceComparator());
					Trade successTrade = pricedTrades.get(0);
					bulletin.setSuccessTrade(successTrade);
					bulletin.setAgreedPrice(successTrade.getOffer());
					bulletin.setTradeStatus(TradeStatus.Agreed);	
					setOverFields(bulletin);
					entityManager.merge(bulletin);
					bulletinAgreeEvent.fire(bulletin);
				} else {
					bulletin.setTradeStatus(TradeStatus.Passed);
					bulletin.setReChooseReason("因无中介报名，此公告已自动流拍！");
					setOverFields(bulletin);
					entityManager.merge(bulletin);
					bulletinPassEvent.fire(bulletin);
				}
			} else if (chooseType.equals(ChooseType.Average)) {
				BigDecimal average = bulletin.getAverage();
				if ( pricedTrades.size() > 0) {
					Collections.sort( pricedTrades, new AverageComparator( average ) );
					Trade successTrade = pricedTrades.get(0);
					bulletin.setSuccessTrade(successTrade);
					bulletin.setAgreedPrice(successTrade.getOffer());
					bulletin.setTradeStatus(TradeStatus.Agreed);//	
					setOverFields(bulletin);
					entityManager.merge(bulletin);
					bulletinAgreeEvent.fire(bulletin);
				} else {
					bulletin.setTradeStatus(TradeStatus.Passed);
					bulletin.setReChooseReason("因无中介报名，此公告已自动流拍！");
					setOverFields(bulletin);
					entityManager.merge(bulletin);
					bulletinPassEvent.fire(bulletin);
				}
			}
		}
		sendWsEvent.fire(bulletin);
	}
	
	
	public void endChooseBulletin(String bulletinId) {
		Bulletin bulletin = entityManager.find(Bulletin.class, bulletinId);
		endChooseWsEvent.fire(bulletin);
//		sendWsMessage(bulletin);
		
	}

	public void startChooseBulletin(String bulletinId) {
		Bulletin bulletin = entityManager.find(Bulletin.class, bulletinId);
		if(updateBulletinChooseTime(bulletin)) {
			endBulletin(bulletin);
		}
//		sendWsMessage(bulletin);
		
	}
	
	
	public void sendWsMessage(Bulletin bulletin) {
		try {
			bulletinEndClientEndPointService.sendMessage(bulletin);
		} catch (DeploymentException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}
	
	public String auditBestChoose(Bulletin bulletin, Trade trade) {
		//避免重复设置
		if(!bulletin.getWayOfChoose().equals(ChooseType.Best) ) return "";
		bulletin.setSuccessTrade(trade);
		bulletin.setTradeStatus(TradeStatus.Agreed);
		setOverFields(bulletin);
		bulletin.setPublished( true );
		entityManager.merge(bulletin);
		bulletinAgreeEvent.fire(bulletin);
		return "auditted";
	}
	
	public String auditAssessChoose(Bulletin bulletin, Trade trade) {
		//避免重复设置
		if(bulletin.getTradeStatus().equals(TradeStatus.Agreed)) return "";
		bulletin.setSuccessTrade(trade);
		bulletin.setTradeStatus(TradeStatus.Agreed);
		setOverFields(bulletin);
		entityManager.merge(bulletin);
		bulletinAgreeEvent.fire(bulletin);
		return "auditted";
	}
	
	@Inject
	UserService userService;
	public String auditCancelSuccessBulletin(Bulletin bulletin) {
		bulletin.setCancelled(true);
		bulletin.setCancelTime(new Date());
		User user = userService.getLoginedUser();
		bulletin.setCancelOperator(user);
		entityManager.merge(bulletin);
		return "auditted";
	}
	
	private void setOverFields(Bulletin bulletin) {
		Calendar cal = Calendar.getInstance();
		bulletin.setOverTime(cal.getTime());
		bulletin.setOverYear(cal.get(Calendar.YEAR));
		bulletin.setOverMonth(cal.get(Calendar.MONTH) + 1);
		bulletin.setOver(true);
	}
	
	public static void clearOverFields(Bulletin bulletin) {
		bulletin.setOverTime(null);
		bulletin.setOverYear(null);
		bulletin.setOverMonth(null);
		bulletin.setOver(false);
	}

	private static int getRandom(int count) {
		Random random = new Random();
		return random.nextInt(count);
	}
	
	private final String GET_INVITE_TRADES_BY_BULLETIN_ID = ""
			+ " SELECT trade  "
			+ " FROM Trade trade "
			+ " WHERE trade.bulletin.id = :bulletinId";
	
	public List<Trade> getInviteTradesByBulletinId (String bulletinId){
		Query query = entityManager.createQuery(GET_INVITE_TRADES_BY_BULLETIN_ID)
				.setParameter("bulletinId", bulletinId);
		List<Trade> resultList = query.getResultList();
		
		Bulletin bulletin = this.getBulletinById( bulletinId );
		Bulletin root = bulletin.getRootConflictingBulletin();
		String rootId = null;
		if ( root == null ) {
			rootId = bulletin.getId();
		} else {
			rootId = root.getId();
		}
		List<String> conflictTrades = queryConflictTrades( rootId );
		List<Trade> returnResultList = new ArrayList<>();
		for ( Trade trade: resultList ) {
			if ( conflictTrades.contains( trade .getId() ) ) {
				continue;
			}
			returnResultList.add( trade );
		}
		
		return returnResultList;
	}
	
	private static final String QUERY_CONFLICT_TRADE_EQL = ""
			+ "	SELECT			b.successTrade.id	"
			+ " FROM			Bulletin b	"
			+ "	WHERE		(	b.conflictingBulletin.id = :rootConflictBulletinId OR b.id = :rootConflictBulletinId ) ";
	public List<String> queryConflictTrades ( String rootId ) { 
		Query query = entityManager.createQuery( QUERY_CONFLICT_TRADE_EQL );
		query.setParameter( "rootConflictBulletinId", rootId );
		return query.getResultList();
	}
	
	@Transactional
	public boolean updateBulletinChooseTime(Bulletin bulletin) {
		bulletinOverTimer.cancelBulletinTimer(bulletin);
		Date now = new Date();
		bulletin.setChooseTime(new Date(now.getTime()));
		try {
			entityManager.merge(bulletin);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}		
	}
	
	private final String GET_OPENED_BULLETINS = ""
			+ " SELECT bulletin  "
			+ " FROM Bulletin bulletin "
			+ " WHERE bulletin.delFlag = 0 "
			+ " AND bulletin.auditResult = 1 "
			+ " AND bulletin.tradeStatus = 'Opened' ";
	public List<Bulletin> getOpenedBulletin(){
		Query query = entityManager.createQuery(GET_OPENED_BULLETINS);
		List<Bulletin> bulletins = query.getResultList();
		return bulletins;
	}
	
	@Inject
	NotificationService notificationService;
	
	@Transactional
	public void cancelAppliedTrades(List<ServiceSupplier> suppliers) {
		List<Bulletin> bulletins = getOpenedBulletin();
		for (Bulletin bulletin : bulletins) {
			List<Trade> trades = getInviteTradesByBulletinId(bulletin.getId());
			for (Trade trade : trades) {
				ServiceSupplier supplier = trade.getSupplier();
				if(isSupplierInList(supplier,suppliers)) {
					if(trade.isApply() && trade.isHasVerify()) {
						trade.setApply(false);
						trade.setApplyTime(null);
						trade.setHasVerify(false);
						entityManager.merge(trade);
						notificationService.sendCancelAppliedBulletinNotification(bulletin, supplier);
					}
				}
			}
		}
	}
	
	public boolean isSupplierInList(ServiceSupplier supplier,List<ServiceSupplier> suppliers) {
		boolean flag = false;
		for (ServiceSupplier serviceSupplier : suppliers) {
			if(serviceSupplier.getId().equals(supplier.getId())) {
				flag = true;
				break;
			}
		}
		return flag;
	}
	
	
	public Bulletin  getBulletinById(String bulletinId) {
		Bulletin bulletin = entityManager.find(Bulletin.class, bulletinId);
		return bulletin;
	} 
	
	@Transactional
	public void deleteBulletinById(String id) {
		Bulletin b = entityManager.find(Bulletin.class, id);
		b.setDelFlag(DataEntity.DEL_FLAG_DELETE);
		entityManager.merge(b);
	}
	/**
	 * 二期后新增
	 * @return
	 */
	public List<Bulletin> getWeeklyBulletins() {
		successBulletinList.refresh();
		successBulletinList.setStartDate(DateUtil.getTimesWeekMorning());
		successBulletinList.setEndDate(DateUtil.getTimesWeekNight());
		successBulletinList.setMaxResults(null);
		List<Bulletin> resultList = successBulletinList.getResultList();
		return resultList;
	}                                                                                                                                                                                                                                    
	public List<Bulletin> getMonthlyBulletins() {
		successBulletinList.refresh();
		successBulletinList.setStartDate(DateUtil.getTimesMonthMorning());
		successBulletinList.setEndDate(DateUtil.getTimesMonthNight());
		successBulletinList.setMaxResults(null);
		return successBulletinList.getResultList();
	}
	
	public List<BulletinStat> bulletinStat(Integer year,String qualificationClassId){
		successBulletinList.refresh();
		successBulletinList.setMaxResults(null);
		List<Integer> months = DateUtil.getTwelveMonths();
		List<BulletinStat> bulletinStats = new ArrayList<>();
		months.stream().forEach(month->{
			String dateStr = year +"-"+month;
			BulletinStat stat = new BulletinStat();
			stat.setDataStr(dateStr);
			List resultList = getBulletinCountByDate(year,month,qualificationClassId);
			stat.setCount(resultList.size());
			BigDecimal totalPrice = new BigDecimal(0);
			Integer totalAppliedCount = 0;
			for (Object object : resultList) {
				Object[] array = (Object[])object;
				BigDecimal price = (BigDecimal)array[0] ;
				if(price == null) price = new BigDecimal(0);
				Integer appliedCount = (Integer)array[1];
				totalPrice = totalPrice.add(price);
				totalAppliedCount += appliedCount;
			}	
			stat.setPriceCount(totalPrice);
			stat.setAppliedCount(totalAppliedCount);
			bulletinStats.add(stat);
		});
 		
		return bulletinStats;
	}
	
	private  String GET_BULLETIN_COUNT_BY_DATE = ""
			+ " SELECT b.price,b.hasAppliedCount																	" 
			+ " FROM  Bulletin b																"
			+ "	JOIN   b.qualificationGroup.groups  qgroup			"
			+ " JOIN   qgroup.qualificationTemplates  template		"
//			+ " JOIN   template.sectors  sector		"
			+ " WHERE b.delFlag = '0' 							"
			+ "	AND   b.over = 1									"
			+ "	AND   b.tradeStatus = 'Agreed'						"
			+ "	AND   b.cancelled = 0 	"
			+ " AND	  b.overYear = :year							"
			+ "	AND	  b.overMonth = :month							"
//			+ "	AND (sector.id = :sectorId	OR :sectorId IS NULL )"
			+ "	AND (template.qualificationClass.id = :qualificationClassId	OR :qualificationClassId IS NULL) ";	
	
	public List getBulletinCountByDate(Integer year,Integer month,String qualificationClassId) {
		List list = entityManager.createQuery(GET_BULLETIN_COUNT_BY_DATE).setParameter("year", year).setParameter("month", month).setParameter("qualificationClassId", qualificationClassId)
				.getResultList();
		return list;
	}
	
	private  String GET_BULLETIN_COUNT_BY_QUALIFICATION_CLASS = ""
			+ " SELECT b.price,b.hasAppliedCount																	" 
			+ " FROM  Bulletin b																"
			+ "	JOIN   b.qualificationGroup.groups  qgroup			"
			+ " JOIN   qgroup.qualificationTemplates  template		"
			+ " WHERE b.delFlag = '0' 							"
			+ "	AND   b.over = 1									"
			+ "	AND   b.tradeStatus = 'Agreed'						"
			+ "	AND   b.cancelled = 0 	"
			+ " AND (b.overTime >= :startDate OR :startDate IS NULL) "
			+ " AND (b.overTime <= :endDate OR :endDate IS NULL) "
//			+ "	AND (sector.id = :sectorId	OR :sectorId IS NULL )"
			+ "	AND (template.qualificationClass.id = :qualificationClassId	OR :qualificationClassId IS NULL) ";	
	
	public List getBulletinCountByQualificationClass(Date startDate ,Date endDate,String qualificationClassId) {
		List list = entityManager.createQuery(GET_BULLETIN_COUNT_BY_QUALIFICATION_CLASS).setParameter("startDate", startDate).setParameter("endDate", endDate).setParameter("qualificationClassId", qualificationClassId)
				.getResultList();
		return list;
	}
	
	private  String GET_BULLETIN_COUNT_BY_AGENT_SERVICE = ""
			+ " SELECT b.price,b.hasAppliedCount																	" 
			+ " FROM  Bulletin b																"
			+ "	JOIN   b.qualificationGroup.groups  qgroup			"
			+ " JOIN   qgroup.agentServices  agentService		"
//			+ " JOIN   template.sectors  sector		"
			+ " WHERE b.delFlag = '0' 							"
			+ "	AND   b.over = 1									"
			+ "	AND   b.tradeStatus = 'Agreed'						"
			+ "	AND   b.cancelled = 0 	"
			+ " AND (b.overTime >= :startDate OR :startDate IS NULL) "
			+ " AND (b.overTime <= :endDate OR :endDate IS NULL) "
//			+ "	AND (sector.id = :sectorId	OR :sectorId IS NULL )"
			+ "	AND (agentService.id = :agentServiceId	OR :agentServiceId IS NULL) ";	
	
	public List getBulletinCountByAgentService(Date startDate ,Date endDate,String agentServiceId) {
		List list = entityManager.createQuery(GET_BULLETIN_COUNT_BY_AGENT_SERVICE).setParameter("startDate", startDate).setParameter("endDate", endDate).setParameter("agentServiceId", agentServiceId)
				.getResultList();
		return list;
	}
	
	private static final String QUERY_ALL_BULLETIN_EQL = ""
			+ "	SELECT			b"
			+ "	FROM			Bulletin b ";
	
	@Transactional
	public void cleanBulletin() {
		Session session = entityManager.unwrap( Session.class );
		long count = 0L;
		ScrollableResults sr = session.createQuery(QUERY_ALL_BULLETIN_EQL).setCacheMode( CacheMode.IGNORE ).scroll( ScrollMode.FORWARD_ONLY );
//
		while (sr.next()) {
			Bulletin bulletin = (Bulletin) sr.get(0);
			QualificationGroup qualificationGroup = bulletin.getQualificationGroup();
			qualificationGroup.setBulletin(bulletin);
			List<QualificationGroup> groups = qualificationGroup.getGroups();
			groups.stream().forEach(group ->{
				group.setBulletin(bulletin);
			});
			session.update(bulletin);
			count++;
			if (count % 200 == 0) {
				session.flush();
			}
		}
		session.flush();
	}
	
	public void generateBulletinDiscardRecord(String bulletinId,String reason,Trade formerTrade,Trade successTrade) {
		BulletinDiscardRecord rec = new BulletinDiscardRecord();
		rec.setBulletinId(bulletinId);
		rec.setReason(reason);
		if(formerTrade != null) {
			rec.setFormerSupplierId(formerTrade.getSupplier().getId());
			rec.setFormerSupplierName(formerTrade.getSupplier().getCnname());
		}
		if(successTrade != null) {
			rec.setSuccessSupplierId(successTrade.getSupplier().getId());
			rec.setSuccessSupplierName(successTrade.getSupplier().getCnname());
		}
		entityManager.persist(rec);
	}
	
	@Inject
	BulletinOverTimer timer;
	public String rechoose(Date oldChooseTime,Bulletin bulletin) {
		Trade successTrade = bulletin.getSuccessTrade();
		if(successTrade != null) {
			successTrade.setHasVerify(false);
//			successTrade.setApplyTime(null);
			//随机重新选取通知前中选中介
			try {//防止调用短信接口出现错误导致报错
				smsService.bulletinZuoFeiNotification(bulletin, successTrade.getSupplier());
			} catch (Exception e) {
				System.out.println("短信发送错误:"+e.getMessage());
			}
		}
		
		entityManager.merge(successTrade);
		bulletin.setSuccessTrade(null);
		bulletin.setTradeStatus(TradeStatus.Opened);
		clearOverFields(bulletin);
		entityManager.merge(bulletin);
		bulletin = entityManager.find(Bulletin.class, bulletin.getId());
		if (!(bulletin.getWayOfChoose().equals(ChooseType.Manual))) {
			timer.registerNewBulletin(bulletin);
		}
		//todo 给报名的中介发送通知短信
		List<Trade> verifiedTrades = bulletin.getVerifiedTrades();
		try {//防止调用短信接口出现错误导致报错
			verifiedTrades.stream().forEach(trade->{
				smsService.bulletinRechooseNotification(oldChooseTime, trade.getBulletin(), trade.getSupplier());
			});
		} catch (Exception e) {
			System.out.println("短信发送错误:"+e.getMessage());
		}
		generateBulletinDiscardRecord(bulletin.getId(), bulletin.getReChooseReason(), successTrade, null);
		return "BulletinList.xhtml";	
		
	}
	
	/**择优、竞价、均价作废顺延功能
	 * @param bulletin
	 * @return
	 */
	public String cancelCurrent(Bulletin bulletin,String reChooseReason) {
		bulletin = entityManager.find(Bulletin.class, bulletin.getId());
		bulletin.setReChooseReason(reChooseReason);
		Trade replaceSuccessTrade = null;
		Trade formerSuccessTrade = bulletin.getSuccessTrade();
		if(formerSuccessTrade != null) {
			//为了不影响报名中介数量
			formerSuccessTrade.setHasVerify(false);
//			formerSuccessTrade.setApplyTime(null);
		}
		entityManager.merge(formerSuccessTrade);
		ChooseType wayOfChoose = bulletin.getWayOfChoose();
		
		if(wayOfChoose.equals(ChooseType.Best)) {
			List<Trade> bestTrades = bulletin.getBestTrades();
			bestTrades.remove(formerSuccessTrade);
			bulletin.setBestTrades(bestTrades);
			
			bestTrades.sort(new BestChooseScoreComparator());
			if(bestTrades.size() >=1) {
				replaceSuccessTrade = bestTrades.get(0);
			}
			
		}else {
			List<Trade> pricedTrades = bulletin.getPricedTrades();
			if (wayOfChoose.equals(ChooseType.Average)){
				BigDecimal average = bulletin.getAverage();
				pricedTrades.remove(formerSuccessTrade);
				Collections.sort( pricedTrades, new AverageComparator( average ) );
			}else if(wayOfChoose.equals(ChooseType.Price)) {
				pricedTrades.remove(formerSuccessTrade);
				Collections.sort(pricedTrades, new PriceComparator());
			}
			if(pricedTrades.size() >=1) {
				replaceSuccessTrade = pricedTrades.get(0);
				bulletin.setAgreedPrice(replaceSuccessTrade.getOffer());
			}
		}
		if(replaceSuccessTrade != null) {
			bulletin.setSuccessTrade(replaceSuccessTrade);
			entityManager.merge(bulletin);
			//发送短信
			
			try {//防止调用短信接口出现错误导致报错	
				 //前中选中介 作废短信
				smsService.bulletinZuoFeiNotification(bulletin, formerSuccessTrade.getSupplier());
				//通知短信
				if(wayOfChoose.equals(ChooseType.Best)) {
					smsService.bestBulletinSYToSupplierNotification(bulletin, replaceSuccessTrade.getSupplier());
					smsService.bestBulletinSYToBuyerNotification(bulletin, replaceSuccessTrade.getSupplier());
				}else {
					smsService.priceBulletinSYToSupplierNotification(bulletin, replaceSuccessTrade.getSupplier());
					smsService.priceBulletinSYToBuyerNotification(bulletin, replaceSuccessTrade.getSupplier());
				}
				
			} catch (Exception e) {
				System.out.println("短信发送错误:"+e.getMessage());
			}
			generateBulletinDiscardRecord(bulletin.getId(), bulletin.getReChooseReason(), formerSuccessTrade, replaceSuccessTrade);
			return "BulletinList.xhtml";	
			
			
		}else return "";
		
	}
	
	
	public static boolean isInVerifiedTrades(User supplier,List<Trade> trades) {
		boolean flag = false;
		//lambda 表达式中使用局部变量提示错误
//		trades.stream().forEach(trade->{
//			ServiceSupplier tradeSupplier = trade.getSupplier();
//			if(supplier.getId().equals(tradeSupplier.getId())) {
//				newflag = true;
//			}
//		});
		
		for (Trade trade : trades) {
			ServiceSupplier tradeSupplier = trade.getSupplier();
			if(supplier.getId().equals(tradeSupplier.getId())) {
				flag = true;
				break;
			}
		}
		return flag;
	}
	DateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
	private static final String QUERY_BULLETIN_PROJECT_ID_MAP_EQL = ""
			+ "		SELECT			b.id, b.projectName,b.buyer.cnname,b.chooseTime "
			+ "		FROM			Bulletin b	"
			+ "		WHERE			b.projectName LIKE CONCAT('%', CONCAT( :projectName, '%' ) )	";
	public List<BulletinSelectItem> getBulletinProjectMap( String projectName ) {
		Query query = entityManager.createQuery( QUERY_BULLETIN_PROJECT_ID_MAP_EQL );
		query.setParameter( "projectName", projectName );
		List<Object[]> resultList = query.setMaxResults( 10 ).getResultList();
		List<BulletinSelectItem> list = new ArrayList<>();
		for ( Object[] objects : resultList ) {
			String key = (String) objects[0];
			String value = (String) objects[1];
			String buyer = (String) objects[2];
			Date chooseTime = (Date) objects[3];
			list.add( new BulletinSelectItem( key, value,buyer, sf.format(chooseTime)) );
		}
		return list;
	}
	private static final String QUERY_BEST_TRADES_NAMES_ID_SQL = ""
	+ "		SELECT 			listagg( su.cnname,',' )within group(order by su.id) "
	+ "		FROM			System_User su, ServiceSupplier ss, Trade t, Bulletin_Trade bt "
	+ "		WHERE			su.id = ss.id "
	+ "		AND				ss.id = t.supplier_id "
	+ "		AND				t.id = bt.besttrades_id"
	+ "		AND				bt.bulletin_id = :bulletinId	 ";
	public String getBestTradeSupplierNames(String bulletinId) {
		return (String)entityManager.createNativeQuery(QUERY_BEST_TRADES_NAMES_ID_SQL).setParameter("bulletinId", bulletinId).getSingleResult();
	}

	@Inject
	RuntimeService				runtimeService;

	@Inject
	TaskService					taskService;
	@Transactional
	public void startBuyerBestChooseEvaluationProcess( Bulletin bulletin ) {
	
//		bulletin = entityManager.find(Bulletin.class, bulletin.getId());
		System.out.println("状态"+bulletin.getTradeStatus());
		if ( bulletin.getTradeStatus().equals( TradeStatus.Waiting ) ) {
			HashMap<String, Object> variables = new HashMap<String, Object>();
			variables.put( "buyer", bulletin.getBuyer().getUsername() );
			variables.put( "buyerRegion", bulletin.getBuyer().getRegion() );
			variables.put( "bulletinId", bulletin.getId() );
			variables.put( "projectName", bulletin.getProjectName() );
			variables.put( "chooseTime", bulletin.getChooseTime() );
			variables.put( "supplierRegion", bulletin.getBuyer().getRegion() );
			ProcessInstance process = runtimeService.startProcessInstanceByKey( "bestChooseAudit", bulletin.getId(), variables );
		}
	}
	
}
